package com.hakgc.rent.dao;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 数据库操作的基类，提取公共的数据操作方法
 * @author Xing.Jiejian
 * @date 2020/10/19
 */
public abstract class BaseDao {
    /**
     * 三个数据库操作对象
     */
    protected Connection conn;
    protected PreparedStatement ps;
    protected ResultSet rs;

    /**
     * 可变参数 Object...params
     *  表示，这个方法可以传入0到任意个Object类型的数据，在方法内部将params当作Object[]使用
     * 调用方法时可以是
     *      1) executeUpdate(String)
     *      2) executeUpdate(String,Object,Object...)
     *      3) executeUpdate(String,Object[])
     *
     *
     * 公共的执行增删改操作的方法
     *
     * @param sql 要执行的SQL(可以使用“?”绑定参数)
     * @param params 要绑定的参数(参数个数必须和SQL中的“?”数量一致)
     * @return 执行SQL影响的行数
     */
    public int executeUpdate(String sql,Object...params){
        try (Connection conn = getConn();
             // 预编译的Statement对象
             PreparedStatement ps = conn.prepareStatement(sql)) {
            // 给"?"占位符绑定数据
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return -1;
    }


    /**
     * 公共的处理查询的方法
     * 注意：在ResultSet没有解析完之前不能关闭Connection和PreparedStatement
     *
     * @param sql 要执行的SQL(可以使用“?”绑定参数)
     * @param params 要绑定的参数(参数个数必须和SQL中的“?”数量一致)
     * @return 结果集对象
     */
    public ResultSet executeQuery(String sql, Object... params) {
        try {
            getConn();
            // 预编译的Statement对象
            ps = conn.prepareStatement(sql);
            // 给"?"占位符绑定数据
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            rs = ps.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 获取唯一值的数据库操作
     * @param sql 要执行的SQL(可以使用“?”绑定参数)
     * @param params 要绑定的参数(参数个数必须和SQL中的“?”数量一致)
     * @return 唯一值对象
     */
    public Object uniqueResult(String sql, Object... params){
        try (Connection conn = getConn();
             // 预编译的Statement对象
             PreparedStatement ps = conn.prepareStatement(sql)) {
            // 给"?"占位符绑定数据
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            try(ResultSet rs = ps.executeQuery()){
                while (rs.next()){
                    return rs.getObject(1);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 关闭所有资源对象的方法
     */
    protected void closeAll(){
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps!=null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 数据源对象
     */
    private static DataSource dataSource;

    static {
        // 构造方法不传递参数表示使用默认的C3P0配置(配置文件在src根目录下c3p0-config.xml)
        // DataSource只需要初始化一次
        dataSource = new ComboPooledDataSource();
    }


    /**
     * 获取数据库链接方法(使用C3P0连接池)
     * @return 数据库链接
     */
    protected Connection getConn(){
        try {
            // 从连接池中获取数据库链接
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }


    /**
     * 使用JNDI的方式获取Tomcat中配置的数据源
     * @return
     */
    protected Connection getConn2(){
        try {
            // 初始化上下文
            Context cxt=new InitialContext();
            // java:comp/env/ :JavaEE的默认路径
            DataSource ds=(DataSource)cxt.lookup("java:comp/env/jdbc/rent");
            // 从连接池中获取数据库链接
            conn = ds.getConnection();
        } catch (SQLException | NamingException e) {
            e.printStackTrace();
        }
        return conn;
    }


}
